package cpagraph;

import java.util.Vector;


import cpaparser.OSMNode;

public class Node
{
	public OSMNode osmNode;
	public Vector<Node> adjacents;
	public Vector<Edge> incidents;
	public Vector<Bus> busses;
	public boolean isBusStation;
	
	/**
	 * initialize newly created Node object using existing OSMNode.
	 * @param n existing OSMNode to be encapsulated by new Node object.
	 */
	public Node(OSMNode n)
	{
		osmNode = n;
		adjacents = new Vector<Node>();
		incidents = new Vector<Edge>();
		busses = new Vector<Bus>();
		isBusStation =
			   n.containsTagValue("highway", "bus_stop")
			|| n.containsTagValue("amenity", "bus_station");
	}
	
	public Node()
	{
		osmNode = null;
		adjacents = new Vector<Node>();
		incidents = new Vector<Edge>();
		busses = new Vector<Bus>();
		isBusStation = false;
	}
	
	/**
	 * A node can be merged into another node by adding
	 * all properties to the calling node (from within
	 * this method is called).
	 * If at least one of those two nodes to be merged are
	 * marked as bus stations, then the resulting node
	 * will also be marked as bus station. A warning is
	 * printed, if both nodes are bus stops.
	 * 
	 * This method is only used by the XmlParser.
	 * 
	 * Use with caution! You might want to test this method
	 * before you use it in your code!
	 * 
	 * @param n node to merge into this node
	 */
	public void mergeNode(Node n)
	{
		if (isBusStation && n.isBusStation)
		{
			System.out.println("warning! Merging two bus stops: " + osmNode.id + "+" + n.osmNode.id);
		}
		else if (n.isBusStation)
		{
			isBusStation = true;
			osmNode = n.osmNode;
		}
		
		for (int i = 0; i < n.incidents.size(); i++)
		{
			if (!incidents.contains(n.incidents.elementAt(i)))
				incidents.add(n.incidents.elementAt(i));
			if (!adjacents.contains(n.adjacents.elementAt(i)))
				adjacents.add(n.adjacents.elementAt(i));
		}
		
		for (int i = 0; i < n.busses.size(); i++)
		{
			if (!busses.contains(n.busses.elementAt(i)))
				busses.add(n.busses.elementAt(i));
		}
	}
	
	/**
	 * unsure about best implementation of equals().
	 * Now using default.
	 */
	/*public boolean equals(Node n)
	{
		return osmNode.id.equals(n.osmNode.id);
	}*/
	
	/**
	 * Search for edge between this and node n.
	 * 
	 * @param n second node incident to edge
	 * @return edge connecting this node and node n
	 */
	public Edge getEdge(Node n)
	{
		if (!adjacents.contains(n))
			return null;
		
		Edge f = null;
		int edgeCount = 0;
		for (int i = 0; i < incidents.size(); i++)
		{
			Edge e = incidents.elementAt(i);
			
			if (e.isIncidentTo(n))
			{
				f = e;
				edgeCount++;
			}
		}
		
		if (edgeCount == 0)
			System.out.println("oops! Didnt find edge in getEdge().");
		else if (edgeCount > 1)
			System.out.println("duplicated edge! a=" + this.osmNode.id + " b=" + n.osmNode.id);
		
		return f;
	}
	
	/**
	 * When the list of the nodes' adjacent nodes is not empty,
	 * then this node is supposed to be connected to the graph somehow.
	 * @return false, if not connected to any node, otherwise returns true.
	 */
	public boolean isConnected()
	{
		return !adjacents.isEmpty();
	}
	
	/**
	 * Disconnects this node from another node.
	 * If isAdjacentTo() returns false, no changes
	 * will be applied. Otherwise the nodes are
	 * deleted from one anothers' list of adjacents,
	 * the connecting edge is removed from both
	 * lists of incidents.
	 * @param n node to be disconnected from
	 */
	/*public void disconnectFrom(Node n)
	{
		if (isAdjacentTo(n))
		{
			Edge e = this.getEdge(n);
			
			incidents.remove(e);
			n.incidents.remove(e);
			
			adjacents.remove(n);
			n.adjacents.remove(this);
		}
	}*/
	
	/**
	 * Checks if node n is contained in list of adjacent nodes.
	 * 
	 * @param n node to check about
	 * @return true, if node n is contained in list of adjacents, false otherwise
	 */
	public boolean isAdjacentTo(Node n)
	{
		return adjacents.contains(n);
	}
	
	/**
	 * Checks if bus b is contained in list of busses.
	 * 
	 * @param b bus to check
	 * @return true, if bus b is contained in list of busses
	 */
	public boolean isAssignedTo(Bus b)
	{
		return busses.contains(b);
	}
	
	/**
	 * Adds Bus b to list of busses.
	 * 
	 * @param b Bus to add
	 */
	public void assignBus(Bus b)
	{
		if (!busses.contains(b))
		{
			busses.add(b);
		}
	}
	
	/**
	 * Formats name of the busstop and ref to a string
	 * that can be stored to a file.
	 * @return name of stop and ref in a string
	 */
	public String toRecord()
	{
		String stopName = osmNode.getTag("name");
		if (stopName == null)
		{
			stopName = "";
			System.out.println("no name stop: " + osmNode.id);
		}
		
		String ref = osmNode.getTag("ref");
		if (ref == null)
			ref = "no stop";
		
		return stopName + " " + ref;
	}
	
	/**
	 * For debugging purposes. Prints some attributes.
	 */
	public void print()
	{
		System.out.println(osmNode.id);
		System.out.print("adjacents, incidents: " + Integer.toString(adjacents.size()) + ", ");
		System.out.println(incidents.size());
		System.out.print("is bus station: ");
		System.out.println(isBusStation);
	}
	
	/**
	 * Some more information than print().
	 */
	public void printmore()
	{
		System.out.println(osmNode.id);
		System.out.print("adjacents (" + Integer.toString(adjacents.size()) + "): ");
		for (int i = 0; i < adjacents.size(); i++) System.out.print(adjacents.elementAt(i).osmNode.id + " ");
		System.out.print("\nbusses (" + Integer.toString(busses.size()) + "): ");
		for (int i = 0; i < busses.size(); i++) System.out.print(busses.elementAt(i).number + " ");
		System.out.print("\nis bus station: ");
		System.out.println(isBusStation);
	}
}
